﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Autodesk.DesignScript.Geometry;

namespace DeepSpace.Analysis
{
    public class Triangle
    {
        private Point _p1;
        public Point P1
        {
            get {return _p1;}
        }

        private Point _p2;
        public Point P2
        {
            get {return _p2;}
        }

        private Point _p3;
        public Point P3
        {
            get {return _p3;}
        }

        public Point CenterPoint
        {
            get 
            {
                return  Point.ByCoordinates((_p1.X + _p2.X + _p3.X) / 3,
                                            (_p1.Y + _p2.Y + _p3.Y) / 3,
                                            (_p1.Z + _p2.Z + _p3.Z) / 3);
            }
        }

        public Surface Surface
        {
            get
            {
                var polyCurve = PolyCurve.ByPoints(new Point[] { _p1, _p2, _p3 }, true);
                return Surface.ByPatch(polyCurve);
            }
        }

        public Vector Normal
        {
            get
            {
                var v1 = _p1.AsVector();
                var v2 = _p2.AsVector();
                var v3 = _p3.AsVector();

                var v4 = v2.Subtract(v1);
                var v5 = v3.Subtract(v1);
                var normal = v4.Cross(v5);
                return normal.Normalized();
            }
        }

        [Autodesk.DesignScript.Runtime.IsVisibleInDynamoLibrary(false)]
        private Triangle(Point p1, Point p2, Point p3)
        {
            _p1 = p1;
            _p2 = p2;
            _p3 = p3;
        }

        public static Triangle ByThreePoints(Point p1, Point p2, Point p3)    
        {
            return new Triangle(p1, p2, p3);
        }

        public IList<Triangle> SubdevideTriangle()
        {
            var v1 = Vector.ByTwoPoints(_p1, _p2).Scale(0.5);
            var v2 = Vector.ByTwoPoints(_p1, _p3).Scale(0.5);
            var p4 = Point.ByCoordinates(_p1.X + v1.X, _p1.Y + v1.Y, _p1.Z + v1.Z);
            var p5 = Point.ByCoordinates(_p1.X + v2.X, _p1.Y + v2.Y, _p1.Z + v2.Z);
            var p6 = Point.ByCoordinates(_p1.X + v1.X + v2.X, _p1.Y + v1.Y + v2.Y, _p1.Z + v1.Z + v2.Z);

            var triangles = new List<Triangle>
            {
                Triangle.ByThreePoints(_p1, p4, p5),
                Triangle.ByThreePoints(p4, p6, p5),
                Triangle.ByThreePoints(p4, _p2, p6),
                Triangle.ByThreePoints(p5, p6, _p3),
            };

            return triangles;
        }
    }

    public static class SkyFactorAnalysis
    {
        public static IList<int> GetSkeyFactorForPoints(IList<Point> points, IList<Vector> vectors, Geometry geometry)
        {
            var skyFactors = new int[points.Count];

            for (int i = 0; i < points.Count(); ++i)
            {
                var value = 0;
                foreach (var vec in vectors)
                {
                    var ray = Line.ByStartPointDirectionLength(points[i], vec, 100000);
                    if (!ray.DoesIntersect(geometry))
                    {
                        value = value + 1;
                    }
                }

                skyFactors[i] = value;
            }

            return skyFactors;
        }

        public static IList<Triangle> CreateGeodesicDome(double edgeLength, double radius)
        {
            var centerPoint = Point.ByCoordinates(0, 0, 0);
            var triangles = CreateIcosahedronByPointAndRadius(radius);

            while (triangles[0].P1.DistanceTo(triangles[0].P2) > edgeLength)
            {
                triangles = triangles.SelectMany(t => t.SubdevideTriangle())
                                     .Where(t => t.CenterPoint.Z >= 0)
                                     .ToList();
            }

            for (int i = 0; i < triangles.Count; ++i)
            {
                var triangle = triangles[i];
                var p1 = triangle.P1;
                var p2 = triangle.P2;
                var p3 = triangle.P3;

                bool scaled = false;
                if (p1.DistanceTo(centerPoint) < radius)
                {
                    scaled = true;
                    p1 = p1.AsVector().Normalized().Scale(radius).AsPoint();
                }

                if (p2.DistanceTo(centerPoint) < radius)
                {
                    scaled = true;
                    p2 = p2.AsVector().Normalized().Scale(radius).AsPoint();
                }
                
                if (p3.DistanceTo(centerPoint) < radius)
                {
                    scaled = true;
                    p3 = p3.AsVector().Normalized().Scale(radius).AsPoint();
                }

                if (scaled)
                    triangles[i] = Triangle.ByThreePoints(p1, p2, p3);
            }

            return triangles;
        }

        public static IList<Triangle> CreateIcosahedronByPointAndRadius(double radius)
        {
            var R = radius / 2;
            var d1 = Math.PI / 10.0; 
            var d2 = d1 * 2.0;
            var r = R / Math.Sin(d2);
            var h = r * Math.Cos(d2);
            var cx = r * Math.Cos(d1);
            var cy = r * Math.Sin(d1);
            var t1 = Math.Sqrt(radius * radius - r * r);
            var t2 = Math.Sqrt((h + r) * (h + r) - h * h);
            var z1 = (t2 - t1) / 2;
            var z2 = z1 + t1;

            var p1 = Point.ByCoordinates(0, 0, z2);
            var p2 = Point.ByCoordinates(0, r, z1);
            var p3 = Point.ByCoordinates(cx, cy, z1);
            var p4 = Point.ByCoordinates(R, -h, z1);
            var p5 = Point.ByCoordinates(-R, -h, z1);
            var p6 = Point.ByCoordinates(-cx, cy, z1);
            var p7 = Point.ByCoordinates(0, -r, -z1);
            var p8 = Point.ByCoordinates(-cx, -cy, -z1);
            var p9 = Point.ByCoordinates(-R, h, -z1);
            var p10 = Point.ByCoordinates(R, h, -z1);
            var p11 = Point.ByCoordinates(cx, -cy, -z1);
            var p12 = Point.ByCoordinates(0, 0, -z2);

            var triangles = new List<Triangle> { 
                Triangle.ByThreePoints(p1, p3, p2),  
                Triangle.ByThreePoints(p1, p2, p6),
                Triangle.ByThreePoints(p1, p6, p5),  
                Triangle.ByThreePoints(p1, p5, p4),
                Triangle.ByThreePoints(p1, p4, p3),  
                Triangle.ByThreePoints(p3, p4, p11),
                Triangle.ByThreePoints(p3, p11, p10),  
                Triangle.ByThreePoints(p3, p10, p2),
                Triangle.ByThreePoints(p2, p10, p9),  
                Triangle.ByThreePoints(p2, p9, p6),
                Triangle.ByThreePoints(p6, p9,p8),  
                Triangle.ByThreePoints(p6, p8, p5),
                Triangle.ByThreePoints(p5, p8, p7),  
                Triangle.ByThreePoints(p5, p7, p4),
                Triangle.ByThreePoints(p4, p7, p11),  
                Triangle.ByThreePoints(p12, p10, p11),
                Triangle.ByThreePoints(p12, p11, p7),  
                Triangle.ByThreePoints(p12, p7, p8),
                Triangle.ByThreePoints(p12, p8, p9),  
                Triangle.ByThreePoints(p12, p9, p10)
            };

            return triangles;
        }
    }
}
